#
#    Copyright 2006 Andrew Wilkinson <aw@cs.york.ac.uk>.
#
#    This file is part of PyScobble (http://lastfm.indiegigs.co.uk/pyscrobble)
#
#    PyScobble is free software; you can redistribute it and/or modify
#    it under the terms of the GNU Lesser General Public License as published by
#    the Free Software Foundation; either version 2.1 of the License, or
#    (at your option) any later version.
#
#    PyScobble is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Lesser General Public License for more details.
#
#    You should have received a copy of the GNU Lesser General Public License
#    along with PyScobble; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#

import datetime
import os
import xml
import threading
import time

try:
    import sqlite3
except ImportError:
    raise ImportError, "PyScrobble requires at least Python 2.5"

from scrobble.exceptions import *
from scrobble.utils import *

dbversion = 1

def createDatabase():
    if hasattr(threading.currentThread(), "scrobble_db"):
        return threading.currentThread().scrobble_db
    db = sqlite3.connect(os.path.expanduser('~') + os.sep + ".pyscrobble.cache")
    db.row_factory = sqlite3.Row

    c = db.cursor()
    try:
        r = c.execute("SELECT id FROM version")
    except sqlite3.OperationalError:
        c = db.cursor()
        c.execute("CREATE TABLE version (id)")
        c.execute("INSERT INTO version (id) VALUES (?)", (dbversion, ))
        c.execute("CREATE TABLE user_profiles (id, cluster, username, url, realname, mbox_sha1sum, homepage, country, age, playcount, registered, statsreset, updated)")
        c.execute("CREATE TABLE user_friends (username, friends, updated)")
        c.execute("CREATE TABLE user_neighbours (username, neighbours, updated)")
        c.execute("CREATE TABLE artist_similar (name, similar, updated)")
        c.execute("CREATE TABLE scrobble_cache (username, password, name, artist, mbid, scrobbletime)")
        db.commit()
    else:
        row = r.fetchone()
        if row["id"] != dbversion:
            db.close()
            os.unlink(os.path.expanduser('~') + os.sep + ".pyscrobble.cache")
            return createDatabase()

    threading.currentThread().scrobble_db = db
    return db

def getUserProfile(username, count=0):
    db = createDatabase()

    c = db.cursor()
    r = c.execute("SELECT * FROM user_profiles WHERE username=?", (unicode(username), ))
    row = r.fetchone()

    if row is not None:
        if row["updated"] < datetime.date.today().toordinal() - 7:
            r = c.execute("DELETE FROM user_profiles WHERE username=?", (username, ))
        else:
            dbHit()
            return {"id": row["id"], "cluster": row["cluster"], "username": row["username"], "url": row["url"], "realname": row["realname"], "mbox_sha1sum": row["mbox_sha1sum"], "homepage": row["homepage"], "country": row["country"], "age": row["age"], "playcount": row["playcount"], "registered": datetime.date.fromordinal(row["registered"]), "statsreset": datetime.date.fromordinal(row["statsreset"]) if row["statsreset"] is not None else None, "updated": row["updated"]}

    dom = getDom(audioscrobbler_webservices, "/1.0/user/%s/profile.xml" % (encode(username.replace(" ", "+")), ))

    profile = dom.getElementsByTagName("profile")
    if len(profile) == 0:
        if count < 5:
            time.sleep(30)
            return getUserProfile(username, count+1)
        else:
            raise LastFMError
    profile = profile[0]

    id = int(profile.getAttribute("id"))
    cluster = int(profile.getAttribute("cluster"))
    username = profile.getAttribute("username")

    try:
        url = getDomText(profile.getElementsByTagName("url")[0].childNodes)
    except IndexError:
        url = None
    try:
        realname = getDomText(profile.getElementsByTagName("realname")[0].childNodes)
    except IndexError:
        realname = None
    try:
        mbox_sha1sum = getDomText(profile.getElementsByTagName("mbox_sha1sum")[0].childNodes)
    except IndexError:
        mbox_sha1sum = None
    try:
        homepage = getDomText(profile.getElementsByTagName("homepage")[0].childNodes)
    except IndexError:
        homepage = None
    try:
        country = getDomText(profile.getElementsByTagName("country")[0].childNodes)
    except IndexError:
        country = None

    try:
        age = int(getDomText(profile.getElementsByTagName("age")[0].childNodes))
    except IndexError:
        age = None
    try:
        playcount = int(getDomText(profile.getElementsByTagName("playcount")[0].childNodes))
    except IndexError:
        playcount = None

    registered = profile.getElementsByTagName("registered")[0]
    registered = datetime.date.fromtimestamp(int(registered.getAttribute("unixtime")))
    try:
        statsreset = profile.getElementsByTagName("statsreset")[0]
    except IndexError:
        statsreset = None
    else:
        statsreset = datetime.date.fromtimestamp(int(statsreset.getAttribute("unixtime")))

    c = db.cursor()
    r = c.execute("INSERT INTO user_profiles (id, cluster, username, url, realname, mbox_sha1sum, homepage, country, age, playcount, registered, statsreset, updated) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)", (id, cluster, username, url, realname, mbox_sha1sum, homepage, country, age, playcount, registered.toordinal(), statsreset.toordinal() if statsreset is not None else None, datetime.date.today().toordinal()))

    db.commit()

    return getUserProfile(username)

def getUserFriends(username, count=0):
    db = createDatabase()

    c = db.cursor()
    r = c.execute("SELECT * FROM user_friends WHERE username=?", (unicode(username), ))
    row = r.fetchone()

    if row is not None:
        if row["updated"] < datetime.date.today().toordinal() - 7:
            r = c.execute("DELETE FROM user_friends WHERE username=?", (username, ))
        else:
            dbHit()
            return eval(row["friends"])

    try:
        dom = getDom(audioscrobbler_webservices, "/1.0/user/%s/friends.xml" % (encode(username.replace(" ", "+")), ))
    except xml.parsers.expat.ExpatError:
        raise ValueError, "User %s does not exist" % (username, )

    friends = dom.getElementsByTagName("friends")
    if len(friends) == 0:
        if count < 5:
            time.sleep(30)
            return getUserFriends(username, count+1)
        else:
            raise LastFMError
    friends = [e.getAttribute("username") for e in friends[0].getElementsByTagName("user")]

    c = db.cursor()
    r = c.execute("INSERT INTO user_friends (username, friends, updated) VALUES (?,?,?)", (username, str(friends), datetime.date.today().toordinal()))

    db.commit()

    return friends

def getUserNeighbours(username, count=0):
    db = createDatabase()

    c = db.cursor()
    r = c.execute("SELECT * FROM user_neighbours WHERE username=?", (unicode(username), ))
    row = r.fetchone()

    if row is not None:
        if row["updated"] < datetime.date.today().toordinal() - 7:
            r = c.execute("DELETE FROM user_neighbours WHERE username=?", (username, ))
        else:
            dbHit()
            return eval(row["neighbours"])

    try:
        dom = getDom(audioscrobbler_webservices, "/1.0/user/%s/neighbours.xml" % (encode(username.replace(" ", "+")), ))
    except xml.parsers.expat.ExpatError:
        raise ValueError, "User %s does not exist" % (username, )

    neighbours = dom.getElementsByTagName("neighbours")
    if len(neighbours) == 0:
        if count < 5:
            time.sleep(30)
            return getUserNeighbours(username, count+1)
        else:
            raise LastFMError
    neighbours = [e.getAttribute("username") for e in neighbours[0].getElementsByTagName("user")]

    c = db.cursor()
    r = c.execute("INSERT INTO user_neighbours (username, neighbours, updated) VALUES (?,?,?)", (username, str(neighbours), datetime.date.today().toordinal()))

    db.commit()

    return neighbours

def getSimilarArtists(name, count=0):
    db = createDatabase()

    c = db.cursor()
    r = c.execute("SELECT * FROM artist_similar WHERE name=?", (unicode(name), ))
    row = r.fetchone()

    if row is not None:
        if row["updated"] < datetime.date.today().toordinal() - 7:
            r = c.execute("DELETE FROM artist_similar WHERE name=?", (name, ))
        else:
            dbHit()
            return eval(row["similar"])

    try:
        dom = getDom(audioscrobbler_webservices, "/1.0/artist/%s/similar.xml" % (encode(name.replace(" ", "+")), ))
    except xml.parsers.expat.ExpatError:
        raise ValueError, "Artist %s does not exist" % (name, )

    similar = dom.getElementsByTagName("similarartists")
    if len(similar) == 0:
        if count < 5:
            time.sleep(30)
            return getSimilarArtists(username, count+1)
        else:
            raise LastFMError
    similar = [getDomText(e.getElementsByTagName("name")[0].childNodes) for e in similar[0].getElementsByTagName("artist")]

    c = db.cursor()
    r = c.execute("INSERT INTO artist_similar (name, similar, updated) VALUES (?,?,?)", (name, str(similar), datetime.date.today().toordinal()))

    db.commit()

    return similar

def getScrobbleCache():
    db = createDatabase()
    c = db.cursor()
    r = c.execute("SELECT * FROM scrobble_cache ORDER BY scrobbletime")
    t = []
    for row in r.fetchall():
        u = User(row["username"], passwd=row["passwordd"])
        t = Track(row["artist"], row["name"], row["mbid"])
        d = datetime.datetime.fromtimestamp(row["scrobbletime"])
        t.append((u, t, d))
    return t

def deleteFromCache(user, track, time):
    return

def addToScrobbleCache(user, track, time):
    db = createDatabase()
    c = db.cursor()
    r = c.execute("INSERT INTO scrobble_cache (user,, friends, updated) VALUES (?,?,?)", (username, str(friends), datetime.date.today().toordinal()))

    db.commit()

def encode(s):
    return s.replace("&", "%2526").replace("?", "%3F")
